package core;

import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;

import core.domain.IOUtil;
import core.domain.PeerDesciptor;
import core.domain.RegistrationStatus;
import core.domain.Torrent;

public class InMemoryTorrentDAO implements ITorrentDAO {

	// no good singleton! read effective Java 2 , item 3. (enum singleton)
	private static InMemoryTorrentDAO instance = null;
	
	private ConcurrentMap<Torrent, Set<PeerDesciptor>> store;
	
	private InMemoryTorrentDAO()  {
		store = new ConcurrentHashMap<Torrent, Set<PeerDesciptor>>();
	}
	
	public static synchronized InMemoryTorrentDAO getInstance(){
		if(instance == null){
			instance = new InMemoryTorrentDAO();			
		}
		return instance;
	}
	
	@Override
	public RegistrationStatus reg(Torrent torrent, PeerDesciptor peerDescription) {
//		Torrent incomingTorrent = IOUtil.getInstance(Torrent.class).stringToObject(torrent);
//		PeerDesciptor peerDescription = IOUtil.getInstance(PeerDesciptor.class).stringToObject(peerInfo);
		return storeValue(torrent, peerDescription);
	}

	private RegistrationStatus storeValue(Torrent torrent, PeerDesciptor peerInformation){
		//TODO: return unavailable here if the tracker is gone offline
		if(store.containsKey(torrent)){
			store.get(torrent).add(peerInformation);
			return RegistrationStatus.DUPLICATE;
		} else {
			Set<PeerDesciptor> peerSet = Collections.newSetFromMap(new ConcurrentHashMap<PeerDesciptor, Boolean>());
			peerSet.add(peerInformation);
			store.put(torrent,peerSet);
			return RegistrationStatus.SUCCESSFUL;
		}
	}
	
	/**
	 * Strings are marshalled Torrent objects.
	 */
	@Override
	public Set<Torrent> getFiles() {
		Set<Torrent> result = store.keySet();
		
		return result == null ? new HashSet<Torrent>() : result;
	}

	@Override
	public Set<PeerDesciptor> getPeersForFile(Torrent torrent) {
		Set<PeerDesciptor> result = store.get(torrent);
		return result == null? new HashSet<PeerDesciptor>() : result;
	}

	@Override
	public Set<PeerDesciptor> getAllPeers() {
		Set<PeerDesciptor> resultSet = new HashSet<PeerDesciptor>();
		for(Torrent torrent : store.keySet()){
			resultSet.addAll(store.get(torrent)); //set does not contain duplicates... :)
		}
		return resultSet;
	}
	
	/**
	 * removes peer from the given torrent
	 * @param torrent
	 * @param peer
	 */
	public void removePeerForTorrent(Torrent torrent, PeerDesciptor peer){
		if(store.get(torrent)!= null){
			store.get(torrent).remove(peer);
		}
	}

	/**
	 * removes the peer from all the torrents it was assigned to
	 * @param peer
	 */
	public void removePeer(PeerDesciptor peer){
		for(Torrent torrent : store.keySet()){
			store.get(torrent).remove(peer);
		}
	}
	
	public void reset() {
		store = new ConcurrentHashMap<Torrent, Set<PeerDesciptor>>();
	}
}
